package system

import (
	"fmt"
	"syscall"
	"time"

	"github.com/shaocongcong/apollo/agent"
	"github.com/shaocongcong/apollo/plugins/data/system"
	"go.uber.org/zap"
)

type NetStats struct {
	ps        PS
	stop      chan bool
	appname   string
	Interval  int    `toml:"interval"`
	TransName string `toml:"trans_name"`
}

func (netstats *NetStats) Gather() error {
	netconns, err := netstats.ps.NetConnections()
	if err != nil {
		return fmt.Errorf("error getting net connections info: %s", err)
	}
	counts := make(map[string]int)
	counts["UDP"] = 0

	// TODO: add family to tags or else
	tags := map[string]string{}
	for _, netcon := range netconns {
		if netcon.Type == syscall.SOCK_DGRAM {
			counts["UDP"] += 1
			continue // UDP has no status
		}
		c, ok := counts[netcon.Status]
		if !ok {
			counts[netcon.Status] = 0
		}
		counts[netcon.Status] = c + 1
	}

	fields := map[string]interface{}{
		"tcp_established": counts["ESTABLISHED"],
		"tcp_syn_sent":    counts["SYN_SENT"],
		"tcp_syn_recv":    counts["SYN_RECV"],
		"tcp_fin_wait1":   counts["FIN_WAIT1"],
		"tcp_fin_wait2":   counts["FIN_WAIT2"],
		"tcp_time_wait":   counts["TIME_WAIT"],
		"tcp_close":       counts["CLOSE"],
		"tcp_close_wait":  counts["CLOSE_WAIT"],
		"tcp_last_ack":    counts["LAST_ACK"],
		"tcp_listen":      counts["LISTEN"],
		"tcp_closing":     counts["CLOSING"],
		"tcp_none":        counts["NONE"],
		"udp_socket":      counts["UDP"],
	}

	metric := &system.Metric{
		Name:     "netstat",
		Fields:   fields,
		Tags:     tags,
		Time:     time.Now().Unix(),
		Interval: netstats.Interval,
	}

	metric.Tags["app"] = netstats.appname
	agent.Writer(netstats.TransName, []*system.Metric{metric})
	return nil
}

func (netstats *NetStats) start() error {
	netstats.appname = agent.Name()
	ticker := time.NewTicker(time.Duration(netstats.Interval) * time.Second)
	defer func() {
		if err := recover(); err != nil {
			agent.Logger.Error("netstats init", zap.Any("err", err))
		}
	}()
	defer ticker.Stop()

	for {
		select {
		case <-netstats.stop:
			return nil
		case <-ticker.C:
			netstats.Gather()
			continue
		}
	}
}

// Init ...
func (netstats *NetStats) Init() error {
	go netstats.start()
	return nil
}

// Start ...
func (mem *NetStats) Start() error {
	return nil
}

// Close ...
func (netstats *NetStats) Close() error {
	netstats.stop <- true
	return nil
}

func init() {
	agent.AddCollector("netstat", &NetStats{
		stop: make(chan bool, 1),
		ps:   &systemPS{},
	})
}
